package com.fancy.common.utils.retry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * 异常重试工具类
 * 
 * @author xinfeng.hu 2013-3-28下午9:09:50
 * @version 1.0.0
 * @param <T>
 * @category 杭州广桥网络技术有限公司(商业先知)
 */
public class RetryUtil {

	/**
	 * 日志记录器
	 */
	private static Logger logger = LoggerFactory.getLogger(RetryUtil.class);

	/**
	 * 私有化构造器
	 */
	private RetryUtil() {
		// nothing
	}

	/**
	 * 以异常重试的方式执行操作
	 * 
	 * @param handler
	 * @param maxRetry
	 * @return
	 */
	public static <T> T retry(final int maxRetry, final RetryHandler<T> handler) {
		Assert.notNull(handler, "handler not be null");
		Assert.isTrue(maxRetry > 0, "maxRetry not be less than 1");

		for (int retryCount = 0; retryCount < maxRetry; retryCount++) {
			try {
				return handler.execute();
			} catch (Exception ex) {
				if (handler.retryHandler(ex, retryCount)) {
					logger.warn("execute error,retryCount:{},{}", retryCount, ex);
				} else {
					throw new RuntimeException(ex);
				}
			}
		}
		return null;
	}

	/**
	 * 以异常重试的方式执行操作
	 * 
	 * @param handler
	 * @param maxRetry
	 * @return
	 */
	public static <T> T retry(final long millis, final int maxRetry, final RetryHandler<T> handler) {
		Assert.notNull(handler, "handler not be null");
		Assert.isTrue(maxRetry > 0, "maxRetry not be less than 1");

		for (int retryCount = 0; retryCount < maxRetry; retryCount++) {
			try {
				return handler.execute();
			} catch (Exception ex) {
				if (handler.retryHandler(ex, retryCount)) {
					logger.warn("execute error,retryCount:{},{}", retryCount, ex);
				} else {
					throw new RuntimeException(ex);
				}
			}
			try {
				Thread.sleep(millis);
			} catch (InterruptedException e) {
				logger.warn("execute error,retry:sleep");
			}
		}
		return null;
	}

	/**
	 * 执行控制器
	 * 
	 * @author xinfeng.hu 2013-3-29上午10:14:37
	 * @version 1.0.0
	 * @category 杭州广桥网络技术有限公司(商业先知)
	 */
	public static abstract class RetryHandler<T> {

		/**
		 * 这里写具体的执行逻辑<br>
		 * 如果需要精细控制重试类型请重载，{@link retryHandler}
		 * 
		 * @return
		 * @throws Exception
		 */
		public abstract T execute() throws Exception;

		/**
		 * 异常重试控制器
		 * 
		 * @param exception
		 * @param executionCount
		 * @return
		 */
		public boolean retryHandler(final Exception exception, final int executionCount) {
			return true;
		}

	}

}
